﻿#region Using

using System.Diagnostics;
using SAPbouiCOM;
using SapFramework.Interface.Controls.Items.Base;
using SapFramework.Interface.Events;

using Form = SapFramework.Interface.Controls.Forms.Base.Form;
using Item = SapFramework.Interface.Controls.Items.Base.Item;

#endregion

namespace SapFramework.Interface.Controls.Items.Specific
{
    /// <summary>
    /// Realiza o encapsulamento de um objeto do tipo <see cref="SAPbouiCOM.Matrix"/> da UI API do SAP Business One
    /// </summary>
    public sealed class Matrix : Item, SAPbouiCOM.Matrix
    {
        #region Eventos

        /// <summary>
        /// Ocorre depois que o item recebe o foco.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_GOT_FOCUS )]
        public event EventVoidItem OnGotFocus;

        /// <summary>
        /// Ocorre depois que o item perde o foco.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_LOST_FOCUS )]
        public event EventVoidItem OnLostFocus;

        /// <summary>
        /// Eventos que ocorre quando um item perdeu o fóco e a validação é obrigatória para ele, mas ainda não ocorreu.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_VALIDATE )]
        public event EventBoolItem OnBeforeValidate;

        /// <summary>
        /// Eventos que ocorre quando um item perdeu o fóco e a validação é obrigatória para ele, e esta validação já ocorreu.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_VALIDATE )]
        public event EventVoidItem OnAfterValidate;

        /// <summary>
        /// Evento disparado quando o botão do mouse foi liberado em cima de um item, ou seja, quando o botão do mouse está em cima, mas nenhuma ação foi tomada ainda.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_ITEM_PRESSED )]
        public event EventBoolItem OnBeforeItemPressed;

        /// <summary>
        /// Evento disparado quando o botão do mouse foi liberado em cima de um item, ou seja, quando o botão do mouse está em cima, e qualquer ação do SBO já foi realizada.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_ITEM_PRESSED )]
        public event EventVoidItem OnAfterItemPressed;

        /// <summary>
        /// Ocorre quando um item na ComboBox foi selecionado, porém nenhuma ação foi tomada ainda.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_COMBO_SELECT )]
        public event EventBoolItem OnBeforeComboSelect;

        /// <summary>
        /// Ocorre quando um item na ComboBox foi selecionado, e qualquer ação do SBO já foi executada.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas
        /// </remarks>
        [Event( BoEventTypes.et_COMBO_SELECT )]
        public event EventVoidItem OnAfterComboSelect;

        /// <summary>
        /// Um Linked Button foi clicado na matriz, mas nenhuma ação ainda foi tomada.
        /// </summary>
        [Event( BoEventTypes.et_MATRIX_LINK_PRESSED )]
        public event EventBoolItem OnBeforeMatrixLinkPressed;

        /// <summary>
        /// Um Linked Button foi clicado na matriz, e o SBO já realizou as ações dele.
        /// </summary>
        [Event( BoEventTypes.et_MATRIX_LINK_PRESSED )]
        public event EventVoidItem OnAfterMatrixLinkPressed;

        /// <summary>
        /// Ocorre antes de os dados da base de dados serem carregados para o DataSource da Matriz.
        /// </summary>
        /// <remarks>
        /// Este evento ocorre somente em Matrizes definidas pelo usuário!
        /// </remarks>
        [Event( BoEventTypes.et_MATRIX_LOAD )]
        public event EventBoolItem OnBeforeMatrixLoad;

        /// <summary>
        /// Ocorre após os dados da base de dados serem carregados para o DataSource da Matriz.
        /// </summary>
        /// <remarks>
        /// Este evento ocorre somente em Matrizes definidas pelo usuário!
        /// </remarks>
        [Event( BoEventTypes.et_MATRIX_LOAD )]
        public event EventVoidItem OnAfterMatrixLoad;

        /// <summary>
        /// Ocorre quando os dados de uma matriz estão para serem transferidos para o DataSource dela.
        /// </summary>
        [Event( BoEventTypes.et_DATASOURCE_LOAD )]
        public event EventBoolItem OnBeforeDatasourceLoad;

        /// <summary>
        /// Ocorre quando os dados de uma matriz já foram transferidos para o DataSource dela.
        /// </summary>
        [Event( BoEventTypes.et_DATASOURCE_LOAD )]
        public event EventVoidItem OnAfterDatasourceLoad;

        /// <summary>
        /// Antes de uma lista na matriz ser recolhida/expandida
        /// </summary>
        [Event( BoEventTypes.et_MATRIX_COLLAPSE_PRESSED )]
        public event EventBoolItem OnBeforeMatrixCollapsePressed;

        /// <summary>
        /// Após uma lista na matriz ser recolhida/expandida
        /// </summary>
        [Event( BoEventTypes.et_MATRIX_COLLAPSE_PRESSED )]
        public event EventVoidItem OnAfterMatrixCollapsePressed;

        /// <summary>
        /// Evento que ocorre quando um botão de ChooseFromList foi clicado, mas ela ainda não foi invocada.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas e Linhas
        /// </remarks>
        [Event( BoEventTypes.et_CHOOSE_FROM_LIST )]
        public event EventBoolChooseFromList OnBeforeChooseFromList;

        /// <summary>
        /// Evento que ocorre quando um botão de ChooseFromList foi clicado, e ela já está a mostra.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas e Linhas
        /// </remarks>
        [Event( BoEventTypes.et_CHOOSE_FROM_LIST )]
        public event EventVoidChooseFromList OnAfterChooseFromList;

        /// <summary>
        /// Disparado antes da exibição do menu que ocorre quando um clique com o botão direito do mouse é feito sobre um item.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas e Linhas
        /// </remarks>
        [Event( BoEventTypes.et_RIGHT_CLICK )]
        public event EventBoolContextMenu OnBeforeRightClick;

        /// <summary>
        /// Disparado após a exibição do menu que ocorre quando um clique com o botão direito do mouse é feito sobre um item.
        /// </summary>
        /// <remarks>
        /// Disponivel para Colunas e Linhas
        /// </remarks>
        [Event( BoEventTypes.et_RIGHT_CLICK )]
        public event EventVoidContextMenu OnAfterRightClick;

        #endregion

        /// <summary>
        /// Guarda uma referencia para a parte específica do item
        /// </summary>
        private readonly SAPbouiCOM.Matrix mMatrix;

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="aForm">O formulário ao qual o item pertence</param>
        /// <param name="aItemUniqueId">Id único do Item do Form do SAP Business One</param>
        public Matrix( Form aForm, string aItemUniqueId ) : base( aForm, aItemUniqueId, BoFormItemTypes.it_MATRIX )
        {
            mMatrix = mItem.Specific as SAPbouiCOM.Matrix;
        }

        #region Implementation of IMatrix

        /// <summary>
        /// Adiciona uma linha a Matrix (e a seu DBDataSource, respectivamente).
        /// </summary>
        /// <param name="aRowCount">O número de linhas que deve adicionar.</param>
        /// <param name="aPosition">A posição das novas linhas. Iniciada em 0. Aceita -1, fazendo com que adicione as linhas pelo final.</param>
        public void AddRow( int aRowCount = 1, int aPosition = -1 )
        {
            mMatrix.AddRow( aRowCount, aPosition );
        }

        /// <summary>
        /// Exclui uma linha da Matrix.
        /// </summary>
        /// <param name="aRowNum">O número da linha que se deseja excluir.</param>
        public void DeleteRow( int aRowNum )
        {
            mMatrix.DeleteRow( aRowNum );
        }

        /// <summary>
        /// Exclui todos os dados da Matrix.
        /// </summary>
        public void Clear()
        {
            mMatrix.Clear();
        }

        /// <summary>
        /// Alonga as colunas até o tamanho limite da Matrix, redimensionando delas.
        /// </summary>
        /// <remarks>
        /// O comprimento de uma Matrix pode ser maior do que o comprimento total de todas as colunas, criando um espaço entre a ultima coluna e o final da Matrix.
        /// 
        /// O SAP Business One supera este problema redimensionando todas as colunas sempre que a Matrix é redesenhada. 
        /// Isto pode acontecer inesperadamente quando você trabalha com uma Matrix.
        /// 
        /// Sendo assim, é recomendado invocar o método <see cref="AutoResizeColumns"/> sempre que você terminar de criar uma Matrix, 
        /// assim garantindo que as colunas estejam sempre alongadas até o tamanho total da Matrix.
        /// </remarks>
        public void AutoResizeColumns()
        {
            mMatrix.AutoResizeColumns();
        }

        /// <summary>
        /// Seleciona a linha na Matrix
        /// </summary>
        /// <remarks>
        /// Valido somente para Matrizes do usuário.
        /// 
        /// O número da linha deve ser superior a zero e não deve exceder o número de linhas na matriz.
        /// 
        /// O método se comporta conforme descrito pela propriedade <see cref="SelectionMode"/>
        /// </remarks>
        /// <param name="aRowNum">Número da linha para selecionar. Deve ser maior que zero e menor ou igual a quantidade de linhas da matrix.</param>
        /// <param name="aSelect">Indica se deve selecionar a linha. <c>true</c> para selecionar e <c>false</c> para des-selecionar.</param>
        /// <param name="aMultiSelect">Indica se seleções anteriores na Matrix devem ser mantidas. <c>true</c> para deixa-las selecionadas e <c>false</c> para não deixa-las selecionadas.</param>
        public void SelectRow( int aRowNum, bool aSelect, bool aMultiSelect )
        {
            Debug.Assert( aRowNum > 0 || aRowNum <= mMatrix.RowCount, "Linha está fora do limite permitido para Matrix" );

            mMatrix.SelectRow( aRowNum, aSelect, aMultiSelect );
        }

        /// <summary>
        /// Retorna o índice da primeira linha seleciona dentro de um alcance (range) específico na matriz.
        /// </summary>
        /// <param name="aFirstRow">A primeira linha a qual iniciar procurando</param>
        /// <param name="aOrderType">O método para procurar pela próxima linha selecionada</param>
        /// <returns><c>-1</c> quando nenhuma linha estiver selecionada. Do contrário, retorna o índice da linha selecionada.</returns>
        public int GetNextSelectedRow( int aFirstRow = 0, BoOrderType aOrderType = BoOrderType.ot_SelectionOrder )
        {
            Debug.Assert( aOrderType == BoOrderType.ot_SelectionOrder && aFirstRow > -1, "Quando método de seleção for BoOrderType.ot_SelectionOrder, aFirstRow inicia de zero" );
            Debug.Assert( aOrderType == BoOrderType.ot_RowOrder && aFirstRow > 0, "Quando método de seleção for BoOrderType.ot_RowOrder, aFirstRow inicia de um" );

            return mMatrix.GetNextSelectedRow( aFirstRow, aOrderType );
        }

        /// <summary>
        /// Indica quando uma dada lina se encontra selecionada.
        /// </summary>
        /// <param name="aRowNum">Número da linha</param>
        /// <returns></returns>
        public bool IsRowSelected( int aRowNum = 1 )
        {
            return mMatrix.IsRowSelected( aRowNum );
        }

        /// <summary>
        /// Limpa a seleção de linhas ou colunas da Matrix.
        /// </summary>
        public void ClearSelections()
        {
            mMatrix.ClearSelections();
        }

        /// <summary>
        /// Recupera os dados de uma linha especifica e os coloca no DataSource ligado a linha.
        /// 
        /// Utilize este método para recuperar dados de uma linha pelo DataSource ligado a coluna. Isto lhe permite trabalhar com colunas não editaveis utilizando o DataSoruce.
        /// </summary>
        /// <remarks>
        /// Relevante apenas para matrizes definidas pelo usuário.
        /// </remarks>
        /// <param name="aRowNum">Número da linha</param>
        public void GetLineData( int aRowNum )
        {
            mMatrix.GetLineData( aRowNum );
        }

        /// <summary>
        /// Atualiza a linha da matriz com os valores atuais do DataSource ligado a ela.
        /// 
        /// Utilize este método para atualizar os dados da linha a partir do DataSource das colunas. Isto lhe permite trabalhar com campos
        /// </summary>
        /// <remarks>
        /// Este método atualiza apenas uma das linhas da Matrix com os dados do DataSource.
        /// </remarks>
        /// <param name="aRowNum">Número da linha</param>
        public void SetLineData( int aRowNum )
        {
            mMatrix.SetLineData( aRowNum );
        }

        /// <summary>
        /// Carrega a interface do usuário com os dados atuais que estão no DataSource da Matriz.
        /// </summary>
        /// <remarks>
        /// Relevante para formulários do sistema apenas.
        /// </remarks>
        public void LoadFromDataSource()
        {
            mMatrix.LoadFromDataSource();
        }

        /// <summary>
        /// Envia os dados atuais da interface do usuário para o DataSource ligado à matriz, conforme segue:
        /// <list type="number">
        /// <item>Limpa o DataSource;</item>
        /// <item>Copia cada uma das linhas da matriz para o registro correspondente no DataSource;</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// Todos os valores prévios no DataSource são excluidos.
        /// 
        /// Se existirem DataSources do usuário ligados as colunas da matriz, estas irão conter os valores da última linha na matriz.
        /// 
        /// Relevante para formulários do sistema apenas.
        /// </remarks>
        public void FlushToDataSource()
        {
            mMatrix.FlushToDataSource();
        }

        /// <summary>
        /// Retorna a informação sobre a matriz (os dados e/ou os metadados) no formato de XML.
        /// 
        /// O esquema do XML encontra-se disponivel utilizando o método <see cref="GetSchema"/>
        /// </summary>
        /// <param name="aMatrixXmlSelect">Especifica o XML para retornar</param>
        /// <returns></returns>
        public string SerializeAsXML( BoMatrixXmlSelect aMatrixXmlSelect = BoMatrixXmlSelect.mxs_All )
        {
            return mMatrix.SerializeAsXML( aMatrixXmlSelect );
        }

        /// <summary>
        /// Retorna o esquema para o XML que é retornado pelo método <see cref="SerializeAsXML"/>
        /// </summary>
        /// <remarks>
        /// O esquema do XML é o mesmo para todos os objetos da matriz.
        /// 
        /// Você pode utilizar o esquema para criar objetos para gerenciar os dados da matriz, com a ajuda da ferramenta XML Schema Definition, da linha de comando (xsd.exe),
        /// o qual é parta da Framework do .Net.
        /// </remarks>
        /// <returns></returns>
        public string GetSchema()
        {
            return mMatrix.GetSchema();
        }

        /// <summary>
        /// Retorna uma instâncea da parte específica (única) de um item, tal como 
        /// <see cref="SAPbouiCOM.EditText"/> ou <see cref="SAPbouiCOM.CheckBox"/>, para uma dada célula da matriz.
        /// </summary>
        /// <param name="aColumn">Índice da coluna.</param>
        /// <param name="aRow">Índice da linha</param>
        /// <remarks>
        /// Ésta é a maneira recomendada para se recuperar o conteúdo de uma célula.
        /// </remarks>
        /// <returns></returns>
        public object GetCellSpecific( object aColumn, int aRow )
        {
            return mMatrix.GetCellSpecific( aColumn, aRow );
        }

        /// <summary>
        /// Seta o conteúdo de uma célula sem passar pela validação.
        /// </summary>
        /// <remarks>
        /// Este item não é documentado pela SDK da SAP!
        /// </remarks>
        /// <param name="aRowNum">Número da linha</param>
        /// <param name="aColumnId">Id único da coluna</param>
        /// <param name="aValue">Valor</param>        
        public void SetCellWithoutValidation( int aRowNum, string aColumnId, string aValue )
        {
            mMatrix.SetCellWithoutValidation( aRowNum, aColumnId, aValue );
        }

        /// <summary>
        /// Retorna uma referencia para o objeto de colunas da Matrix.
        /// </summary>
        public Columns Columns
        {
            get { return mMatrix.Columns; }
        }

        /// <summary>
        /// Retorna a quantidade de linhas na Matrix.
        /// </summary>
        /// <remarks>
        /// Esta quantidade de linhas pode ser diferente da quantidade de linhas mostrada no DataSource, pelo fato de ser possivel adicionar umas linhas customizadas as matrizes.
        /// </remarks>
        public int RowCount
        {
            get { return mMatrix.RowCount; }
        }

        /// <summary>
        /// Indica como as linhas podem ser selecionadas na Matrix.
        /// </summary>
        /// <remarks>
        /// Relevante apenas para matrizes definidas pelo usuário.
        /// 
        /// Se esta propriedade for alterar para um modo mais restrito (Auto para único, Nenhum/Único para Nenhum), 
        /// todas as linhas serão des-selecionadas.
        /// </remarks>
        public BoMatrixSelect SelectionMode
        {
            get { return mMatrix.SelectionMode; }
            set { mMatrix.SelectionMode = value; }
        }

        /// <summary>
        /// Layout da Matrix
        /// </summary>
        public BoMatrixLayoutType Layout
        {
            get { return mMatrix.Layout; }
            set { mMatrix.Layout = value; }
        }

        /// <summary>
        /// O número de linhas na matriz, exceto as que foram deletadas ou as linhas que se encontram recolhidas.
        /// 
        /// Este número reflete somente as linhas que podem ser vistas pela interface do usuário.
        /// </summary>
        public int VisualRowCount
        {
            get { return mMatrix.VisualRowCount; }
        }

        /// <summary>
        /// A ordem de tabulação do item
        /// </summary>
        /// <remarks>
        /// Este item não é documentado pela SDK da SAP!
        /// </remarks>
        public int TabOrder
        {
            get { return mMatrix.TabOrder; }
            set { mMatrix.TabOrder = value; }
        }

        /// <summary>
        /// Recupera as coordenadas da célula que está em fóco.
        /// </summary>
        /// <returns></returns>
        public CellPosition GetCellFocus()
        {
            return mMatrix.GetCellFocus();
        }

        /// <summary>
        /// Seta o fóco na célula que está na aRowNum e aColNum especificadas
        /// </summary>
        /// <param name="aRowNum">Número da linha, começando de zero.</param>
        /// <param name="aColNum">Número da coluna, começando de zero.</param>
        public void SetCellFocus( int aRowNum, int aColNum )
        {
            mMatrix.SetCellFocus( aRowNum, aColNum );
        }

        /// <summary>
        /// Propriedades em comum para as células da Grid.
        /// </summary>
        /// <remarks>
        /// Permise deixar colunas e linhas editaveis ou não, ou até alterar suas cores.
        /// </remarks>
        public CommonSetting CommonSetting
        {
            get { return mMatrix.CommonSetting; }
        }

#if !Debug881 && !Release881
        /// <summary>
        /// Carrega a interface do usuário com os dados atuais que estão no DataSource da Matriz.
        /// </summary>
        /// <remarks>
        /// Relevante para formulários do sistema apenas.
        /// </remarks>
        /// <param name="aRebuildMatrix">Indica se deve remontar toda a matriz</param>
        public void LoadFromDataSourceEx( bool aRebuildMatrix )
        {
            mMatrix.LoadFromDataSourceEx( aRebuildMatrix );
        }
#endif
        #endregion
    }
}